package com.example.testdemo.ai;

import org.andengine.extension.tmx.TMXLayer;
import org.andengine.extension.tmx.TMXProperties;
import org.andengine.extension.tmx.TMXTile;
import org.andengine.extension.tmx.TMXTileProperty;
import org.andengine.extension.tmx.TMXTiledMap;

import com.example.testdemo.kinetic.sprite.KineticSprite;

public class TrafficLaw {
	@SuppressWarnings("unused")
	private final String TAG = TrafficLaw.class.getSimpleName();

	public static String TILE_TYPE = "type";
	public static String TILE_HEART = "heart";
	public static String TILE_OBSTACLE = "obstacle";
	public static String TILE_WALL = "wall";
	public static String TILE_WATER = "water";
	public static String TILE_FIRE = "fire";
	public static String TILE_TREE = "tree";

	public static class TileVertex {
		public float x = 0;
		public float y = 0;

		public TileVertex(float x, float y) {
			this.x = x;
			this.y = y;
		}
	}

	class TrafficTile {
		public int row = 0;
		public int column = 0;
		public TMXTile tile = null;
		private boolean heart = false;
		private boolean obstacle = false;
		private boolean wall = false;
		private boolean water = false;
		private boolean fire = false;
		private boolean tree = false;

		public TrafficTile(TMXTiledMap map, TMXTile tile) {
			this.row = tile.getTileRow();
			this.column = tile.getTileColumn();
			this.tile = tile;
			TMXProperties<TMXTileProperty> p = tile.getTMXTileProperties(map);
			if (p != null) {
				heart = p.containsTMXProperty(TILE_TYPE, TILE_HEART);
				obstacle = p.containsTMXProperty(TILE_TYPE, TILE_OBSTACLE);
				wall = p.containsTMXProperty(TILE_TYPE, TILE_WALL);
				water = p.containsTMXProperty(TILE_TYPE, TILE_WATER);
				fire = p.containsTMXProperty(TILE_TYPE, TILE_FIRE);
				tree = p.containsTMXProperty(TILE_TYPE, TILE_TREE);
			}
		}

		public boolean isObstacle(boolean obstacleOnly) {
			if (obstacleOnly) {
				return obstacle;
			} else {
				return heart || obstacle || wall || water || fire || tree;
			}
		}
	}

	private TMXTiledMap mMap = null;
	private TMXLayer mLayer = null;
	private TrafficTile mTiles[][] = null;
	private int mRows = 0;
	private int mColumns = 0;

	public TrafficLaw(TMXTiledMap map) {
		mMap = map;
		mLayer = map.getTMXLayers().get(0);
		mRows = map.getTileRows();
		mColumns = map.getTileColumns();
		initTiles();
	}

	private void initTiles() {
		mTiles = new TrafficTile[mRows][mColumns];
		for (int i = 0; i < mRows; i++) {
			for (int j = 0; j < mColumns; j++) {
				mTiles[i][j] = new TrafficTile(mMap, mLayer.getTMXTile(j, i));
			}
		}
	}

	public void updateTileType(TMXTile tile, boolean obstacle) {
		if (tile.getTMXTileProperties(mMap).containsTMXProperty(TILE_TYPE,
		        TILE_OBSTACLE)) {
			mTiles[tile.getTileRow()][tile.getTileColumn()].obstacle = obstacle;
		} else if (tile.getTMXTileProperties(mMap).containsTMXProperty(
		        TILE_TYPE, TILE_WALL)) {
			mTiles[tile.getTileRow()][tile.getTileColumn()].wall = obstacle;
		} else if (tile.getTMXTileProperties(mMap).containsTMXProperty(
		        TILE_TYPE, TILE_WATER)) {
			mTiles[tile.getTileRow()][tile.getTileColumn()].water = obstacle;
		} else if (tile.getTMXTileProperties(mMap).containsTMXProperty(
		        TILE_TYPE, TILE_FIRE)) {
			mTiles[tile.getTileRow()][tile.getTileColumn()].fire = obstacle;
		} else if (tile.getTMXTileProperties(mMap).containsTMXProperty(
		        TILE_TYPE, TILE_TREE)) {
			mTiles[tile.getTileRow()][tile.getTileColumn()].tree = obstacle;
		}
	}

	final float TOLERANT = 8f;

	public void restrict(boolean ghost, KineticSprite sprite,
	        float diffTextureTrafficTile, TileVertex pre, TileVertex cur) {
		TMXTile t = mLayer.getTMXTileAt(cur.x, cur.y);
		TrafficTile tl = mTiles[t.getTileRow()][t.getTileColumn()];

		t = mLayer.getTMXTileAt(cur.x + sprite.getWidth(), cur.y);
		TrafficTile tr = mTiles[t.getTileRow()][t.getTileColumn()];

		t = mLayer.getTMXTileAt(cur.x, cur.y + sprite.getHeight());
		TrafficTile bl = mTiles[t.getTileRow()][t.getTileColumn()];

		t = mLayer.getTMXTileAt(cur.x + sprite.getWidth(),
		        cur.y + sprite.getHeight());
		TrafficTile br = mTiles[t.getTileRow()][t.getTileColumn()];

		boolean correct = false;
		float toX = 0, toY = 0;
		
		if (cur.y - pre.y > 0) { // down

			final float maxY = bl.tile.getTileY() - bl.tile.getTileHeight();
			if (br.column - bl.column == 0) {
				if (bl.isObstacle(ghost)) {
					correct = true;
					toX = pre.x;
					toY = maxY;
				}
			} else if (br.column - bl.column == 1) {
				if (bl.isObstacle(ghost) || br.isObstacle(ghost)) {
					if (!bl.isObstacle(ghost)
					        && Math.abs(cur.x - bl.tile.getTileX()) < TOLERANT) {
						correct = true;
						toX = bl.tile.getTileX();
						toY = cur.y;
					} else if (!br.isObstacle(ghost)
					        && Math.abs(cur.x - br.tile.getTileX()) < TOLERANT) {
						correct = true;
						toX = br.tile.getTileX();
						toY = cur.y;
					} else {
						correct = true;
						toX = pre.x;
						toY = maxY;
					}
				}
			} else if (br.column - bl.column == 2) {
				TrafficTile center = mTiles[bl.row][bl.column + 1];
				if (center.isObstacle(ghost)) {
					correct = true;
					toX = pre.x;
					toY = maxY;
				}
			}

			if (correct) {
				sprite.setPosition(toX, toY);
			}

		} else if (cur.y - pre.y < 0) { // up

			final float minY = tl.tile.getTileY() + tl.tile.getTileHeight();
			if (tr.column - tl.column == 0) {
				if (tl.isObstacle(ghost)) {
					correct = true;
					toX = pre.x;
					toY = minY;
				}
			} else if (tr.column - tl.column == 1) {
				if (tl.isObstacle(ghost) || tr.isObstacle(ghost)) {
					if (!tl.isObstacle(ghost)
					        && Math.abs(cur.x - tl.tile.getTileX()) < TOLERANT) {
						correct = true;
						toX = tl.tile.getTileX();
						toY = cur.y;
					} else if (!tr.isObstacle(ghost)
					        && Math.abs(cur.x - tr.tile.getTileX()) < TOLERANT) {
						correct = true;
						toX = tr.tile.getTileX();
						toY = cur.y;
					} else {
						correct = true;
						toX = pre.x;
						toY = minY;
					}
				}
			} else if (tr.column - tl.column == 2) {
				TrafficTile center = mTiles[tl.row][tl.column + 1];
				if (center.isObstacle(ghost)) {
					correct = true;
					toX = pre.x;
					toY = minY;
				}
			}

			if (correct) {
				sprite.setPosition(toX, toY);
			}

		} else if (cur.x - pre.x > 0) { // right

			final float maxX = tr.tile.getTileX() - tr.tile.getTileWidth();
			if (br.row - tr.row == 0) {
				if (tr.isObstacle(ghost)) {
					correct = true;
					toX = maxX;
					toY = pre.y;
				}
			} else if (br.row - tr.row == 1) {
				if (tr.isObstacle(ghost) || br.isObstacle(ghost)) {
					if (!tr.isObstacle(ghost)
					        && Math.abs(cur.y - tr.tile.getTileY()) < TOLERANT) {
						correct = true;
						toX = cur.x;
						toY = tr.tile.getTileY();
					} else if (!br.isObstacle(ghost)
					        && Math.abs(cur.y - br.tile.getTileY()) < TOLERANT) {
						correct = true;
						toX = cur.x;
						toY = br.tile.getTileY();
					} else {
						correct = true;
						toX = maxX;
						toY = pre.y;
					}
				}
			} else if (br.row - tr.row == 2) {
				TrafficTile center = mTiles[tr.row + 1][tr.column];
				if (center.isObstacle(ghost)) {
					correct = true;
					toX = maxX;
					toY = pre.y;
				}
			}

			if (correct) {
				sprite.setPosition(toX, toY);
			}

		} else if (cur.x - pre.x < 0) { // left

			final float minX = tl.tile.getTileX() + tl.tile.getTileWidth();
			if (bl.row - tl.row == 0) {
				if (tl.isObstacle(ghost)) {
					correct = true;
					toX = minX;
					toY = pre.y;
				}
			} else if (bl.row - tl.row == 1) {
				if (tl.isObstacle(ghost) || bl.isObstacle(ghost)) {
					if (!tl.isObstacle(ghost)
					        && Math.abs(cur.y - tl.tile.getTileY()) < TOLERANT) {
						correct = true;
						toX = cur.x;
						toY = tl.tile.getTileY();
					} else if (!bl.isObstacle(ghost)
					        && Math.abs(cur.y - bl.tile.getTileY()) < TOLERANT) {
						correct = true;
						toX = cur.x;
						toY = bl.tile.getTileY();
					} else {
						correct = true;
						toX = minX;
						toY = pre.y;
					}
				}
			} else if (bl.row - tl.row == 2) {
				TrafficTile center = mTiles[tl.row + 1][tl.column];
				if (center.isObstacle(ghost)) {
					correct = true;
					toX = minX;
					toY = pre.y;
				}
			}

			if (correct) {
				sprite.setPosition(toX, toY);
			}
		}
	}
}
